/**
 * 
 */
package com.patternity.model;

import static com.patternity.model.Relation.DELEGATION;
import static com.patternity.model.Relation.GENERALISATION;

import java.util.HashMap;
import java.util.Map;

import com.patternity.model.Motif.DeclarationMode;
import com.patternity.model.Motif.Participant;

/**
 * The catalog of known patterns
 * 
 * @author cyrillemartraire
 */
public class Catalog {

	private final Map<String, Pattern> registry = new HashMap<String, Pattern>();

	public Pattern get(final String key) {
		return registry.get(key);
	}

	public Pattern put(final String key, final Pattern value) {
		return registry.put(key, value);
	}

	public final Catalog setup() {
		final Pattern[] patterns = { immutable(), valueObject(), decorator(),
				adapter(), composite(), command(), commandInvoker() };
		for (final Pattern pattern : patterns) {
			registry.put(pattern.getName(), pattern);
		}
		return this;
	}

	public final static Pattern immutable() {
		return new Pattern("Immutable", null);
	}

	public final static Pattern valueObject() {
		return new Pattern("ValueObject", "Immutable", false, null);
	}

	public static Pattern decorator() {
		final Motif motif = new Motif();

		motif.addParticipant(new Participant("Decorator",
				DeclarationMode.SITE_OF_DECLARATION, null));

		motif.addParticipant(new Participant("Component",
				DeclarationMode.DECLARED_BY_ATTRIBUTE_NAMED, "conformsTo"));

		motif.addRelation(new Relation("conforms to", "Decorator", "Component")
				.addStereotype(GENERALISATION));
		motif.addRelation(new Relation("delegates", "Decorator", "Component")
				.addStereotype(DELEGATION));

		motif.addParticipant(new Participant("Leaf",
				DeclarationMode.INFERRED_AS_SUBTYPE_OF_PARTICIPANT, "Component"));
		motif.addRelation(new Relation("implements", "Leaf", "Component")
				.addStereotype(GENERALISATION));
		return new Pattern("Decorator", motif);
	}

	public static Pattern adapter() {
		final Motif motif = new Motif();

		motif.addParticipant(new Participant("Adapter",
				DeclarationMode.SITE_OF_DECLARATION, null));
		motif.addParticipant(new Participant("Component",
				DeclarationMode.DECLARED_BY_ATTRIBUTE_NAMED, "conformsTo"));
		motif.addParticipant(new Participant("Adaptee",
				DeclarationMode.DECLARED_BY_ATTRIBUTE_NAMED, "delegatesTo"));

		motif.addRelation(new Relation("conforms to", "Adapter", "Component")
				.addStereotype(GENERALISATION));
		motif.addRelation(new Relation("delegates", "Adapter", "Adaptee")
				.addStereotype(DELEGATION));

		return new Pattern("Adapter", motif);
	}

	public static Pattern composite() {
		final Motif motif = new Motif();

		motif.addParticipant(new Participant("Composite",
				DeclarationMode.SITE_OF_DECLARATION, null));

		motif.addParticipant(new Participant("Component",
				DeclarationMode.DECLARED_BY_ATTRIBUTE_NAMED, "conformsTo"));

		motif.addParticipant(new Participant("Leaf",
				DeclarationMode.INFERRED_AS_SUBTYPE_OF_PARTICIPANT, "Component"));

		motif.addRelation(new Relation("conforms to", "Composite", "Component")
				.addStereotype(GENERALISATION));
		motif.addRelation(new Relation("children", "Composite", "Component")
				.addStereotype(DELEGATION));
		motif.addRelation(new Relation("implements", "Leaf", "Component")
				.addStereotype(GENERALISATION));
		return new Pattern("Composite", motif);
	}

	public static Pattern command() {
		final Motif motif = new Motif();

		motif.addParticipant(new Participant("Command",
				DeclarationMode.SITE_OF_DECLARATION, "calls"));
		motif.addParticipant(new Participant("Receiver",
				DeclarationMode.DECLARED_BY_ATTRIBUTE_NAMED, "calls"));

		motif.addRelation(new Relation("calls", "Command", "Receiver")
				.addStereotype(DELEGATION));

		// TODO add concrete commands
		return new Pattern("Command", motif);
	}

	public static Pattern commandInvoker() {
		final Motif motif = new Motif();

		motif.addParticipant(new Participant("Command.Invoker",
				DeclarationMode.SITE_OF_DECLARATION, null));
		motif.addParticipant(new Participant("Command",
				DeclarationMode.DECLARED_BY_ATTRIBUTE_NAMED, "invokes"));

		motif.addRelation(new Relation("invokes", "Command.Invoker", "Command")
				.addStereotype(DELEGATION));

		return new Pattern("CommandInvoker", motif);
	}

}
